package com.shiehub.sdk.opengw.encrypt.SM2Util;

import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECFieldElement.Fp;
import org.bouncycastle.math.ec.ECPoint;

/**
 * @Description: SM2
 * @Author: guoleijie@shie.com.cn
 * @Since:2016年9月13日 17:25:00
 */
public class SM2 {

  // 测试参数
  public static final String[] ecc_param = {
      "8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3",
      "787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498",
      "63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A",
      "8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7",
      "421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D",
      "0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2"};

  // 正式参数
  /*
   * public static String[] ecc_param = {
   * "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
   * "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
   * "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
   * "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
   * "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
   * "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" };
   */
  public final BigInteger ecc_p;
  public final BigInteger ecc_a;
  public final BigInteger ecc_b;
  public final BigInteger ecc_n;
  public final BigInteger ecc_gx;
  public final BigInteger ecc_gy;
  public final ECCurve ecc_curve;
  public final ECPoint ecc_point_g;
  public final ECDomainParameters ecc_bc_spec;
  public final ECKeyPairGenerator ecc_key_pair_generator;
  public final ECFieldElement ecc_gx_fieldelement;
  public final ECFieldElement ecc_gy_fieldelement;

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public SM2() {
    this.ecc_p = new BigInteger(ecc_param[0], 16);
    this.ecc_a = new BigInteger(ecc_param[1], 16);
    this.ecc_b = new BigInteger(ecc_param[2], 16);
    this.ecc_n = new BigInteger(ecc_param[3], 16);
    this.ecc_gx = new BigInteger(ecc_param[4], 16);
    this.ecc_gy = new BigInteger(ecc_param[5], 16);

    this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
    this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);

    this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
    this.ecc_point_g = new ECPoint.Fp(this.ecc_curve,
        this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);

    this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve,
        this.ecc_point_g, this.ecc_n);

    ECKeyGenerationParameters ecc_ecgenparam;
    ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec,
        new SecureRandom());

    this.ecc_key_pair_generator = new ECKeyPairGenerator();
    this.ecc_key_pair_generator.init(ecc_ecgenparam);
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public static SM2 Instance() {
    return new SM2();
  }

  /**
   * @Description:
   * @ReturnType：byte[]
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public byte[] sm2GetZ(byte[] userId, ECPoint userKey) {
    SM3Digest sm3 = new SM3Digest();

    int len = userId.length * 8;
    sm3.update((byte) (len >> 8 & 0xFF));
    sm3.update((byte) (len & 0xFF));
    sm3.update(userId, 0, userId.length);

    byte[] p = Util.byteConvert32Bytes(ecc_a);
    sm3.update(p, 0, p.length);

    p = Util.byteConvert32Bytes(ecc_b);
    sm3.update(p, 0, p.length);

    p = Util.byteConvert32Bytes(ecc_gx);
    sm3.update(p, 0, p.length);

    p = Util.byteConvert32Bytes(ecc_gy);
    sm3.update(p, 0, p.length);

    p = Util.byteConvert32Bytes(userKey.getX().toBigInteger());
    sm3.update(p, 0, p.length);

    p = Util.byteConvert32Bytes(userKey.getY().toBigInteger());
    sm3.update(p, 0, p.length);

    byte[] md = new byte[sm3.getDigestSize()];
    sm3.doFinal(md, 0);
    return md;
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey,
      SM2Result sm2Result) {
    BigInteger e = new BigInteger(1, md);
    BigInteger k = null;
    ECPoint kp = null;
    BigInteger r = null;
    BigInteger s = null;
    do {
      do {
        // 正式环境
        /*
         * AsymmetricCipherKeyPair keypair =
         * ecc_key_pair_generator.generateKeyPair();
         * ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)
         * keypair.getPrivate(); ECPublicKeyParameters ecpub =
         * (ECPublicKeyParameters) keypair.getPublic(); k =
         * ecpriv.getD(); kp = ecpub.getQ();
         */

        // 国密规范测试 随机数k
        String kS = "6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F";
        k = new BigInteger(kS, 16);
        kp = this.ecc_point_g.multiply(k);

        System.out.println("计算曲线点X1: "
            + kp.getX().toBigInteger().toString(16));
        System.out.println("计算曲线点Y1: "
            + kp.getY().toBigInteger().toString(16));
        System.out.println("");

        // r
        r = e.add(kp.getX().toBigInteger());
        r = r.mod(ecc_n);
      } while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));

      // (1 + dA)~-1
      BigInteger da_1 = userD.add(BigInteger.ONE);
      da_1 = da_1.modInverse(ecc_n);

      // s
      s = r.multiply(userD);
      s = k.subtract(s).mod(ecc_n);
      s = da_1.multiply(s).mod(ecc_n);
    } while (s.equals(BigInteger.ZERO));

    sm2Result.r = r;
    sm2Result.s = s;
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public void sm2Verify(byte md[], ECPoint userKey, BigInteger r,
      BigInteger s, SM2Result sm2Result) {
    sm2Result.R = null;
    BigInteger e = new BigInteger(1, md);
    BigInteger t = r.add(s).mod(ecc_n);
    if (t.equals(BigInteger.ZERO)) {
      return;
    } else {
      ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s);
      System.out.println("计算曲线点X0: "
          + x1y1.getX().toBigInteger().toString(16));
      System.out.println("计算曲线点Y0: "
          + x1y1.getY().toBigInteger().toString(16));
      System.out.println("");

      x1y1 = x1y1.add(userKey.multiply(t));
      System.out.println("计算曲线点X1: "
          + x1y1.getX().toBigInteger().toString(16));
      System.out.println("计算曲线点Y1: "
          + x1y1.getY().toBigInteger().toString(16));
      System.out.println("");
      sm2Result.R = e.add(x1y1.getX().toBigInteger()).mod(ecc_n);
      System.out.println("R: " + sm2Result.R.toString(16));
      return;
    }
  }
}




